package com.mycompany.app.common.scheduler.quartz.spring;

import java.text.ParseException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.springframework.beans.factory.InitializingBean;

import com.mycompany.app.common.scheduler.dao.IDaemonJobDao;
import com.mycompany.app.common.scheduler.model.DaemonJob;
import com.mycompany.app.common.scheduler.model.DaemonJobTrigger;
import com.mycompany.app.common.scheduler.model.DaemonJobTrigger.IntervalType;
import com.mycompany.app.common.scheduler.quartz.DaemonJobExecutor;
import com.mycompany.app.common.scheduler.quartz.QuartzConstant;
import com.mycompany.app.common.scheduler.quartz.QuartzUtils;

public class DaemonTriggerRegister implements InitializingBean,ITriggerRegister{

    protected final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
            .getLog(this.getClass());

    /*----------------  Spring beans--------------------------------------*/
    private IDaemonJobDao daemonJobDao;

    public IDaemonJobDao getDaemonJobDao() {

        return daemonJobDao;
    }

    public void setDaemonJobDao(IDaemonJobDao daemonJobDao) {

        this.daemonJobDao = daemonJobDao;
    };

    private Scheduler sched;

    public Scheduler getScheduler() {

        return sched;
    }

    public void setScheduler(Scheduler scheduler) {

        this.sched = scheduler;
    }

    /*-------------------------------------------------------=----------------*/

    public void loadAllTrigger() {

        log.info("loading daemon jobs ...");

        List<DaemonJob> jobs = daemonJobDao.getAll();
        if (jobs != null) {
            Iterator<DaemonJob> it = jobs.iterator();
            while (it.hasNext()) {
                DaemonJob daemonJob = (DaemonJob) it.next();
                Set<DaemonJobTrigger> triggers = daemonJob.getTriggers();
                if (triggers != null) {
                    Iterator<DaemonJobTrigger> it2 = triggers.iterator();
                    while (it2.hasNext()) {
                        DaemonJobTrigger trigger = (DaemonJobTrigger) it2
                                .next();
                        if (log.isInfoEnabled())
                            log.info("Daemon Job Trigger name: "
                                    + trigger.getDescription()
                                    + ". Deamon Job name:  "
                                    + daemonJob.getName());

                        if (trigger.getIntervalType() == IntervalType.RIGHT_NOW)
                            continue;

                        try {
                            registerTrigger(trigger);
                        } catch (Exception e) {
                            log.error(e.getMessage(), e);
                        }
                    }
                }

            }
        }

    }

    public void registerTrigger(DaemonJobTrigger daemonJobTrigger) {

        JobDetail jobDetail = null;
        Trigger trigger = null;

        try {
            jobDetail = sched.getJobDetail(getJobName(daemonJobTrigger),
                    QuartzConstant.DAEMON_GROUP);

            trigger = getTrigger(daemonJobTrigger);

            if (jobDetail == null) {
                jobDetail = createJobDetail(daemonJobTrigger);

                getScheduler().scheduleJob(jobDetail, trigger);
            } else {
                trigger.setJobName(jobDetail.getName());
                trigger.setJobGroup(jobDetail.getGroup());

                if (sched.getTrigger(getTriggerName(daemonJobTrigger),
                        QuartzConstant.DAEMON_GROUP) != null) {
                    // getScheduler().unscheduleJob( getTriggerName(sched),
                    // GROUP_NAME );
                    unregisterTrigger(daemonJobTrigger);

                    if (sched.getJobDetail(getJobName(daemonJobTrigger),
                            QuartzConstant.DAEMON_GROUP) == null)
                        sched.scheduleJob(jobDetail, trigger);
                    else
                        sched.scheduleJob(trigger);
                } else
                    getScheduler().scheduleJob(trigger);
            }

            if (log.isInfoEnabled()) {
                log.info("Register successfully. job name is: "
                        + trigger.getFullJobName() + " trigger name is: "
                        + trigger.getFullName());
            }
        } catch (Exception e) {
            log.error("Schedule job failed, JobDetail: " + jobDetail
                    + ", trigger: " + trigger + ". Error reason ", e);
            throw new RuntimeException(e.getMessage());
        }
    };

    public void unregisterTrigger(DaemonJobTrigger daemonJobTrigger) {

        try {
            Trigger trigger = sched.getTrigger(
                    getTriggerName(daemonJobTrigger),
                    QuartzConstant.DAEMON_GROUP);

            if (null != trigger) {
                if (log.isInfoEnabled()) {
                    log.info("Unregister successfully. job name is: "
                            + trigger.getFullJobName() + " trigger name is: "
                            + trigger.getFullName());
                }

                getScheduler().unscheduleJob(getTriggerName(daemonJobTrigger),
                        QuartzConstant.DAEMON_GROUP);
            } else {
                if (log.isInfoEnabled())
                    log.info("Unregister job failed, trigger didnt exist.");
            }
        } catch (SchedulerException e) {
            log.error("Unschedule job failedly. Error reason ", e);
            throw new RuntimeException(e.getMessage());
        }
    }

    private Trigger getTrigger(DaemonJobTrigger daemonJobTrigger)
            throws ParseException {

        Trigger trigger = null;

        if (daemonJobTrigger.getIntervalType() == IntervalType.RIGHT_NOW) {
            trigger = new SimpleTrigger(getTriggerName(daemonJobTrigger),
                    QuartzConstant.DAEMON_GROUP, new Date(), null, 0, 0L);
        } else {
            String cronExp = QuartzUtils
                    .getCronExpression(daemonJobTrigger.getIntervalType(), 0,
                            daemonJobTrigger.getMinute(), daemonJobTrigger
                                    .getHour(), daemonJobTrigger
                                    .getDayOfMonth(), daemonJobTrigger
                                    .getMonth(), daemonJobTrigger
                                    .getDayOfWeek(), daemonJobTrigger.getYear());

            trigger = new CronTrigger(daemonJobTrigger.getDaemonJob().getId()
                    + "_" + daemonJobTrigger.getId(),
                    QuartzConstant.DAEMON_GROUP, cronExp);
        }

        // set misfire policy according to system log. Delayed to next
        // iteration.
        trigger
                .setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);

        return trigger;
    }

    private JobDetail createJobDetail(final DaemonJobTrigger trigger) {

        String jobName = getJobName(trigger);

        JobDetail jobDetail = new JobDetail(jobName,
                QuartzConstant.DAEMON_GROUP, DaemonJobExecutor.class);

        jobDetail.getJobDataMap().put(QuartzConstant.PARAM_DAEMON_JOB_TRIGGER,
                trigger);

        return jobDetail;
    }

    private String getJobName(DaemonJobTrigger trigger) {

        return String.valueOf(trigger.getDaemonJob().getId());
    }

    private String getTriggerName(DaemonJobTrigger daemonJobTrigger) {

        return daemonJobTrigger.getDaemonJob().getId() + "_"
                + daemonJobTrigger.getId();
    }

    public void afterPropertiesSet() throws Exception {

        loadAllTrigger();

    }

}
